package com.log.log.aop;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.log.log.annotation.LogOperation;
import com.log.log.service.OperationLogger;
import com.log.log.utils.AnnotationUtils;
import com.log.log.utils.OperationTypEnum;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

@Aspect
@Component
public class LogOperationAspect {
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private OperationLogger operationLogger;
    private Logger logger = LoggerFactory.getLogger(LogOperationAspect.class);

//    @Transactional(rollbackFor = Exception.class)
    @Around("@annotation(logOperation)")
    public Object logOperation(ProceedingJoinPoint joinPoint, LogOperation logOperation) throws Throwable {
        logger.info("日志记录开始aop1111: {}", logOperation.toString());
//        String userName = request.getAttribute("username").toString();
        String userName="";
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();

        logger.info("开始执行日志逻辑: {}", args);

        Object entity = null;
        if (args.length > 0&&logOperation.handleRelatedEntities()) {
            entity = args[0];
            processEntity(entity,logOperation);


            //处理关联表数据
            List<?> objects = extractRelatedEntities(entity);
            logger.info("开始处理关联表数据：{}",objects);
            if(objects.size()>0){
                for (Object object : objects) {
                    processEntity(object,logOperation);
                }
            }

        }

        if (args.length > 0 && logOperation.operationType() == OperationTypEnum.UPDATEBATCH) {
            logger.info("传入的参数： {}", args.toString());
            List<Map<String, Object>> entityList = (List<Map<String, Object>>) args[0];
            for (Object arg : entityList) {
                processEntity(arg, logOperation);
            }
        }
        if (args.length > 0 && logOperation.operationType() == OperationTypEnum.UPDATE&& !logOperation.handleRelatedEntities()) {
            logger.info("UPDATE操作 {}", "开始");
            entity = args[0];
            processEntity(entity, logOperation);
        }
        if (args.length>0 && logOperation.operationType() == OperationTypEnum.DELETE) {
            String tableName = AnnotationUtils.getTableName(entity.getClass());
            // 利用反射获取类中对应的的字段
            Field primaryKeyField = entity.getClass().getDeclaredField(logOperation.primaryKeyField());
            //将字段的访问权限设置为可访问，以便可以获取私有字段的值
            primaryKeyField.setAccessible(true);

            String primaryKeyValue = primaryKeyField.get(entity).toString();
            entity = args[0];
            operationLogger.log(
                    logOperation.primaryKeyField(),

                    tableName,
                    primaryKeyValue, // 数据主键
                    "null",
                     "null",
                    "null",
                    userName
            );
        }


        // 继续执行目标方法
        Object result = joinPoint.proceed();

        return result;
    }

    //获取关联表对应数据及字段
    private List<?> extractRelatedEntities (Object entity){
        try {
            for (Field declaredField : entity.getClass().getDeclaredFields()) {
                //开启获取私有字段设置为可访问
                declaredField.setAccessible(true);
                //判断是否是list
                if (List.class.isAssignableFrom(declaredField.getType())){
                    Object value = declaredField.get(entity);
                    if (value instanceof List){
                        return (List<?>) value;
                    }
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return Collections.emptyList();
    }

    //处理参数实体
    private void processEntity(Object entity, LogOperation logOperation) throws Throwable {
//        String userName = request.getAttribute("username").toString();
        // 获取实体类和主键字段
        Object oldValue = null;
        // 如果实体对象为空，记录错误并返回
        if (entity == null) {
            logger.error("实体对象为空");
            throw new NullPointerException("实体对象为空");
        }
        //根据调用注解传入的主键名称获取对应的主键值
        Field primaryKeyField = entity.getClass().getDeclaredField(logOperation.primaryKeyField());//获取主键名称
        primaryKeyField.setAccessible(true);
        String primaryKeyValue = primaryKeyField.get(entity).toString();//获取主键的值
        String tableName = AnnotationUtils.getTableName(entity.getClass());
        logger.info("操作表的名称：{}", tableName);
        oldValue = getOldValue(tableName, logOperation.primaryKeyField(), primaryKeyValue); // 从数据库中获取旧值

        // 获取新值
        Object newValue = entity;

        // 比较 oldValue 和 newValue，并记录日志
        logger.info("打印oldValue: {}", oldValue);
        logger.info("打印newValue: {}", newValue);

        if (oldValue != null && newValue != null) {
            compareAndLogChanges(logOperation.operationType(), tableName, (Map<String, Object>) oldValue, newValue, "userName", primaryKeyValue.toString()); // 当前用户需要从上下文中获取
        }
    }

    private Map<String, Object> getOldValue(String tableName, String primaryKeyField, String primaryKeyValue) {
        return operationLogger.queryForMap(tableName, primaryKeyField, primaryKeyValue);
    }

    //进行update的新旧对比以及日志入库
    private void compareAndLogChanges(OperationTypEnum operationType, String dataTableName, Map<String, Object> oldValue, Object newValue, String username, String primaryKeyValue) {
        // 将 newValue 的字段转换为 Map
        Map<String, Object> newValueMap = new HashMap<>();
        //获取注解中对应的变量名称以匹配传入参数的变量名称
        Map<String, String> columnToFieldMap = AnnotationUtils.buildColumnToFieldMap(newValue.getClass());
        // 比较两个对象的字段，并记录变化
        for (Field field : newValue.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            try {
                newValueMap.put(field.getName(), field.get(newValue));
            } catch (IllegalAccessException e) {
                logger.error("Error accessing field value", e);
            }
        }
        for (Map.Entry<String, Object> entry : oldValue.entrySet()) {

            try {
                String fieldName = entry.getKey();
                Object oldFieldValue = entry.getValue();
                String key = columnToFieldMap.get(fieldName);
                Object newFieldValue = newValueMap.get(key);
                if (ObjectUtils.isEmpty(oldFieldValue) || ObjectUtils.isEmpty(newFieldValue)) {
                    continue;
                }
                //目前对于json字段，只能先判断有没有{
                if (newFieldValue.toString().contains("{")) {
                    JSONObject jsonObjectnew = JSON.parseObject(newFieldValue.toString());
                    JSONObject jsonObjectold = JSON.parseObject(oldFieldValue.toString());

                    newFieldValue = jsonObjectnew.toString();
                    oldFieldValue = jsonObjectold.toString();
                }
                if (!Objects.equals(oldFieldValue, newFieldValue)) {
                    operationLogger.log(
                            operationType.name(),
                            dataTableName,
                            primaryKeyValue, // 数据主键
                            fieldName,
                            oldFieldValue != null ? oldFieldValue.toString() : "null",
                            newFieldValue != null ? newFieldValue.toString() : "null",
                            username
                    );
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }


        }
    }
}
